# -*- coding: utf-8 -*-
'''
Created on 06.03.2015

@author: MathiasAr
'''

import sys, os, shutil, filecmp
import codecs


pkgBasePath = os.path.join('..', '..')

def getHtmlPkgName(pkgName):
    for fl in file(os.path.join(pkgBasePath, 'pkgbase.names'), "r").readlines():
        if not fl or fl.startswith("#"): continue
        entry = fl.replace('\t', ' ').split()
        if (len(entry) >= 2):
            if entry[0].strip().lower() == pkgName.lower():
                return entry[1].strip()
    return pkgName

def getPkgsFromHtmlName(htmlName):
    result = []
    for fl in file(os.path.join(pkgBasePath, 'pkgbase.names'), "r").readlines():
        if not fl or fl.startswith("#"): continue
        entry = fl.replace('\t', ' ').split()
        if (len(entry) >= 2):
            if entry[1].strip().lower() == htmlName.lower():
                result.append(entry[0].strip())
    if len(result) == 0:
        result.append(htmlName)
    return result

def parsePkgDeps(file_line):
    if file_line and not file_line.startswith("#") and not file_line.startswith("!"):
        pkgEntry = file_line.split()
        if len(pkgEntry) > 0:
            pkgEntryName = pkgEntry[0].strip()
            if pkgEntryName:
                if len(pkgEntry) > 1:
                    pkgEntryVersion = pkgEntry[1].strip()
                else:
                    pkgEntryVersion = None
                return pkgEntryName, pkgEntryVersion
    return None, None

def parsePkgInfo(file_line):
    if file_line and not file_line.startswith("#"):
        pkgEntry = file_line.split()
        if len(pkgEntry) > 0:
            pkgEntryName = '-'.join(pkgEntry[0].split('-')[0:-1])
            if pkgEntryName:
                pkgEntryVersion = pkgEntry[0].split('-')[-1]
                if len(pkgEntry) > 1: pkgEntryArchive = pkgEntry[1]
                else: pkgEntryArchive = pkgEntry[0]
                if len(pkgEntry) > 2: pkgEntryDesc = ' '.join(pkgEntry[2:])
                else: pkgEntryDesc = ""
                return pkgEntryName, pkgEntryVersion, pkgEntryArchive, pkgEntryDesc
    return None, None, None, None

def getPkgInfo(pkgName):
    pkgEntry = (None, None, None, None, None)
    pkgBasicListFile = os.path.join(pkgBasePath, 'basic.package.list')
    pkgListFile = os.path.join(pkgBasePath, 'extra.package.list')
    if os.path.isfile(pkgListFile):
        fl = ""
        for fl in file(pkgListFile, "r").readlines():
            res = parsePkgInfo(fl)
            if res[0] == pkgName:
                pkgEntry = (res[0], res[1], res[2], res[3], 'extra')
                return pkgEntry
        pass
    if os.path.isfile(pkgBasicListFile):
        fl = ""
        for fl in file(pkgBasicListFile, "r").readlines():
            res = parsePkgInfo(fl)
            if res[0] == pkgName:
                pkgEntry = (res[0], res[1], res[2], res[3], 'basic')
                return pkgEntry
        pass
    return pkgEntry

def getHtmlPkgPath(htmlName):
    return os.path.join(pkgBasePath, '..', 'from_html', "%s" % htmlName)

def getHtmlPkgInfo(htmlName):
    pkgEntry = (None, None, None, None)
    pkgInfoFile = os.path.join(getHtmlPkgPath(htmlName), "%s.info" % htmlName)
    if os.path.isfile(pkgInfoFile):
        fl = ""
        for fl in file(pkgInfoFile, "r").readlines():
            pkgEntry = parsePkgInfo(fl)
            if pkgEntry[0] != None: break
        pass
    return pkgEntry

def makePkg(pkgName, pkgVersion, pkgArchive, pkgUnknown=False):
    pkgHtmlName = getHtmlPkgName(pkgName)
    if not pkgUnknown:
        htmlInfo = getHtmlPkgInfo(pkgName)
        if htmlInfo[0] == None:
            sys.stderr.write('makePkg: can not find package %s in html data base (html name: %s) \n' % (pkgName, pkgHtmlName))
            return False
    
    pkgDir = os.path.join(pkgBasePath, 'extra', pkgName)
    
    if not os.path.isdir(pkgDir):
        shutil.copytree(src=os.path.join(pkgDir, '..', 'SKEL'),
                        dst=pkgDir)
    else:
        sys.stderr.write('makePkg: package directory exists %s\n' % pkgDir)
        
    # copy html informations into package directory
    pkgBaseDir = os.path.join(pkgDir, 'pkgbase')
    if os.path.isdir(pkgBaseDir):
        shutil.rmtree(pkgBaseDir)
    
    if not pkgUnknown:
        
        shutil.copytree(src=getHtmlPkgPath(pkgHtmlName),
                        dst=pkgBaseDir)
        
        if os.path.isfile(os.path.join(pkgBaseDir, 'deps.pkg')):
            # convert HTML dependencies in package dependencies
            deps = codecs.open(os.path.join(pkgDir, 'deps.pkg'), "w", "utf-8")
            for fl in codecs.open(os.path.join(pkgBaseDir, 'deps.pkg'), "r", "utf-8").readlines():
                htmlDepName, depVersion = parsePkgDeps(fl)
                if htmlDepName:
                    depsName = getPkgsFromHtmlName(htmlDepName)
                    for d in depsName:
                        if depVersion:
                            deps.write('%s %s\n' % (d.ljust(30), depVersion))
                        else:
                            deps.write('%s\n' % (d))
                else:
                    deps.write("%s" % fl)
            deps.close()
        else:
            f = file(os.path.join(pkgDir, 'deps.pkg'), "w")
            f.write("")
            f.close()
    else:
        os.makedirs(pkgBaseDir)
        pkgTitle = "%s-%s" % (pkgName, pkgVersion)
        file(os.path.join(pkgBaseDir, '%s.info' % pkgName), "w").write("%s %s %s" % (pkgTitle.ljust(20), pkgArchive.ljust(31), "%s is an unknwon package..." % pkgName))
        
    if not os.path.isfile(os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName)):
        shutil.copy2(src=os.path.join(pkgDir, '..', 'make_SKEL.sh'),
                     dst=os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName))
    else:
        sys.stderr.write('makePkg: make file exists %s\n' % os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName))
    
    if not pkgUnknown:    
        # WinMergeU [/r] [/e] [/f filter] [/x] [/xq] [/s] [/ul] [/ur] [/u] [/wl] [/wr] [/minimize] [/maximize] [/dl leftdesc] [/dr rightdesc] leftpath rightpath [outputpath]
        if (os.system('WinMergeU /wl /dl "%s base install instructions" /dr "%s make file" "%s" "%s"' % (pkgName, pkgName, os.path.join(pkgBaseDir, 'install.txt') , os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName))) != 0):
            sys.stderr.write('makePkg: error while open merge tool...\n')
            return False
        
        if (os.system('WinMergeU /wl /dl "%s base install instructions" /dr "%s setup file" "%s" "%s"' % (pkgName, pkgName, os.path.join(pkgBaseDir, 'install.txt') , os.path.join(pkgDir, 'setup.sh'))) != 0):
            sys.stderr.write('makePkg: error while open merge tool...\n')
            return False
        
        # Notepad++.exe [-multiInst] [-noPlugin] [-ro] [-nosession] [-notabbar] [-n##] [-l$$$] [FILES]
        if (os.system('Notepad++.exe -multiInst -lbash "%s" "%s" "%s" "%s" "%s"' % (os.path.join(pkgDir, '..', '..', 'extra.package.list'),
                                                                                    os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName),
                                                                                    os.path.join(pkgDir, 'setup.sh'),
                                                                                    os.path.join(pkgBaseDir, 'install.txt'),
                                                                                    os.path.join(pkgDir, 'deps.pkg')
                                                                                    )
                      ) != 0
            ):
            sys.stderr.write('makePkg: error while open text editing tool...\n')
            return False
    
    else:
        # Notepad++.exe [-multiInst] [-noPlugin] [-ro] [-nosession] [-notabbar] [-n##] [-l$$$] [FILES]
        if (os.system('Notepad++.exe -multiInst -lbash "%s" "%s" "%s" "%s" "%s"' % (os.path.join(pkgDir, '..', '..', 'extra.package.list'),
                                                                                    os.path.join(pkgBaseDir, '%s.info' % pkgName),
                                                                                    os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName),
                                                                                    os.path.join(pkgDir, 'setup.sh'),
                                                                                    os.path.join(pkgDir, 'deps.pkg')
                                                                                    )
                      ) != 0
            ):
            sys.stderr.write('makePkg: error while open text editing tool...\n')
            return False
    
    sys.stderr.write('Package ready [y/n]?: ')
    answer = sys.stdin.readline().strip()
    if not answer.startswith('y'):
        return False
    
    if not pkgUnknown:
        wgetF = file(os.path.join(pkgDir, '..', 'wget-list'), 'a')
        wgetF.write('\n')
        for fl in file(os.path.join(pkgBaseDir, 'wget-list'), 'r').readlines():
            if fl and not fl.startswith("#"):
                wgetF.write("%s" % fl)
        wgetF.close()
        
    pkgInfo = file(os.path.join(pkgBasePath, 'extra.package.list'), 'a')
    pkgInfo.write('\n')
    pkgInfo.writelines(file(os.path.join(pkgBaseDir, '%s.info' % pkgName), 'r').readlines())
    pkgInfo.close()
    
    return True
    pass



def updatePkg(pkgName, pkgVersion, pkgArchive):
    pkgOldName, pkgOldVersion, pkgOldArchive, pkgOldDesc, pkgDb = getPkgInfo(pkgName)
    pkgHtmlName = getHtmlPkgName(pkgName)
    htmlInfo = getHtmlPkgInfo(pkgName)
    if htmlInfo[0] == None:
        sys.stderr.write('updatePkg: can not find package %s in html data base (html name: %s) \n' % (pkgName, pkgHtmlName))
        return False
    
    pkgDir = os.path.join(pkgBasePath, 'extra', pkgName)
    pkgMakeFile = os.path.join(pkgBasePath, 'extra', 'make_%s.sh' % pkgName)
    
    if pkgDb != 'extra':
        if not os.path.isfile(pkgMakeFile):
            shutil.copy2(os.path.join(pkgBasePath, pkgDb, 'make_%s.sh' % pkgOldName), pkgMakeFile)
        if os.path.isdir(os.path.join(pkgBasePath, pkgDb, pkgOldName)) and not os.path.isdir(pkgDir):
            shutil.copytree(os.path.join(pkgBasePath, pkgDb, pkgOldName), pkgDir)
    
    if not os.path.isdir(pkgDir):
        shutil.copytree(src=os.path.join(pkgDir, '..', 'SKEL'),
                        dst=pkgDir)
    else:
        sys.stderr.write('updatePkg: package directory exists %s\n' % pkgDir)
    
    # copy html informations into package directory
    pkgBaseDir = os.path.join(pkgDir, 'pkgbase')
    pkgBaseDirOld = pkgBaseDir + ".old"
    pkgHtmlDir = getHtmlPkgPath(pkgHtmlName)
    
    # update make file
    # WinMergeU [/r] [/e] [/f filter] [/x] [/xq] [/s] [/ul] [/ur] [/u] [/wl] [/wr] [/minimize] [/maximize] [/dl leftdesc] [/dr rightdesc] leftpath rightpath [outputpath]
    if (os.system('WinMergeU /wl /dl "Skeleton make file" /dr "%s make file" "%s" "%s"' % (pkgName, pkgName, os.path.join(pkgBasePath, 'extra', 'make_SKEL.sh'), pkgMakeFile)) != 0):
        sys.stderr.write('updatePkg: error while open merge tool...\n')
        return False
    
    # update setup file
    # WinMergeU [/r] [/e] [/f filter] [/x] [/xq] [/s] [/ul] [/ur] [/u] [/wl] [/wr] [/minimize] [/maximize] [/dl leftdesc] [/dr rightdesc] leftpath rightpath [outputpath]
    if (os.system('WinMergeU /wl /dl "Skeleton setup file" /dr "%s setup file" "%s" "%s"' % (pkgName, pkgName, os.path.join(pkgBasePath, 'extra', 'SKEL', 'setup.sh'), os.path.join(pkgDir, 'setup.sh'))) != 0):
        sys.stderr.write('updatePkg: error while open merge tool...\n')
        return False
    
    # backup base directory
    if os.path.isdir(pkgBaseDirOld):
        os.removedirs(pkgBaseDirOld)
    if os.path.isdir(pkgBaseDir):
        shutil.move(pkgBaseDir, pkgBaseDirOld)
    
    # copy into base directory from html base
    shutil.copytree(src=pkgHtmlDir,
                    dst=pkgBaseDir)
    
    if os.path.isdir(pkgBaseDirOld):
        # show install instruction change in parallel
        # WinMergeU [/r] [/e] [/f filter] [/x] [/xq] [/s] [/ul] [/ur] [/u] [/wl] [/wr] [/minimize] [/maximize] [/dl leftdesc] [/dr rightdesc] leftpath rightpath [outputpath]
        if (os.system('WinMergeU /wl /wr /dl "%s-%s new install instructions" /dr "%s-%s old install instructions" "%s" "%s" &' % (pkgName, pkgVersion, pkgOldName, pkgOldVersion, os.path.join(pkgBaseDir, 'install.txt'), os.path.join(pkgBaseDirOld, 'install.txt'))) != 0):
            sys.stderr.write('updatePkg: error while open merge tool...\n')
            return False
    
    if os.path.isfile(os.path.join(pkgDir, 'deps.pkg')):
        shutil.copy(os.path.join(pkgDir, 'deps.pkg'), os.path.join(pkgBaseDirOld, 'activedeps.pkg'))
    
    if os.path.isfile(os.path.join(pkgBaseDir, 'deps.pkg')):
        # convert HTML dependencies in package dependencies
        deps = codecs.open(os.path.join(pkgDir, 'deps.pkg'), "w", "utf-8")
        for fl in codecs.open(os.path.join(pkgBaseDir, 'deps.pkg'), "r", "utf-8").readlines():
            htmlDepName, depVersion = parsePkgDeps(fl)
            if htmlDepName:
                depsName = getPkgsFromHtmlName(htmlDepName)
                for d in depsName:
                    if depVersion:
                        deps.write('%s %s\n' % (d.ljust(30), depVersion))
                    else:
                        deps.write('%s\n' % (d))
            else:
                deps.write("%s" % fl)
        deps.close()
    else:
        f = file(os.path.join(pkgDir, 'deps.pkg'), "w")
        f.write("")
        f.close()
    
    if os.path.isfile(os.path.join(pkgBaseDirOld, 'activedeps.pkg')):
        # Merge old and new dependencies
        if (os.system('WinMergeU /wl /dl "%s-%s old dependencies" /dr "%s-%s updated dependencies" "%s" "%s"' % (pkgOldName, pkgOldVersion, pkgName, pkgVersion, os.path.join(pkgBaseDirOld, 'activedeps.pkg'), os.path.join(pkgDir, 'deps.pkg'))) != 0):
            sys.stderr.write('updatePkg: error while open merge tool...\n')
            return False
    
    
    if pkgDb != 'extra':
        # Notepad++.exe [-multiInst] [-noPlugin] [-ro] [-nosession] [-notabbar] [-n##] [-l$$$] [FILES]
        if (os.system('Notepad++.exe -multiInst -lbash "%s" "%s" "%s" "%s" "%s" "%s"' % (os.path.join(pkgDir, '..', '..', '%s.package.list' % pkgDb),
                                                                                        os.path.join(pkgDir, '..', '..', 'extra.package.list'),
                                                                                        os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName),
                                                                                        os.path.join(pkgDir, 'setup.sh'),
                                                                                        os.path.join(pkgBaseDir, 'install.txt'),
                                                                                        os.path.join(pkgDir, 'deps.pkg')
                                                                                        )
                      ) != 0
            ):
            sys.stderr.write('updatePkg: error while open text editing tool...\n')
            return False
    
    else:
        # Notepad++.exe [-multiInst] [-noPlugin] [-ro] [-nosession] [-notabbar] [-n##] [-l$$$] [FILES]
        if (os.system('Notepad++.exe -multiInst -lbash "%s" "%s" "%s" "%s" "%s"' % (os.path.join(pkgDir, '..', '..', 'extra.package.list'),
                                                                                    os.path.join(pkgDir, '..', 'make_%s.sh' % pkgName),
                                                                                    os.path.join(pkgDir, 'setup.sh'),
                                                                                    os.path.join(pkgBaseDir, 'install.txt'),
                                                                                    os.path.join(pkgDir, 'deps.pkg')
                                                                                    )
                      ) != 0
            ):
            sys.stderr.write('updatePkg: error while open text editing tool...\n')
            return False
    
    # remove package source from wget list
    if os.path.isfile(os.path.join(pkgBaseDirOld,'wget-list')):
        wgetOldLines = codecs.open(os.path.join(pkgBaseDirOld,'wget-list'), "r", "utf-8").readlines()
        wgetOld = []
        for w in wgetOldLines:
            if w and not w.startswith('#'):
                wgetOld.append(w.strip())
        
        newWget = codecs.open(os.path.join(pkgBasePath, pkgDb, 'wget-list_new'), "w", "utf-8")
        for fl in codecs.open(os.path.join(pkgDir, '..', '..', 'extra.package.list'), "r", "utf-8"):
            for fl in codecs.open(os.path.join(pkgBasePath, pkgDb, 'wget-list'), "r", "utf-8").readlines():
                if not fl.strip() in wgetOld:
                    newWget.write(fl)
        newWget.close()
        if os.path.isfile(os.path.join(pkgBasePath, pkgDb, 'wget-list')):
            os.remove(os.path.join(pkgBasePath, pkgDb, 'wget-list'))
        shutil.move(os.path.join(pkgBasePath, pkgDb, 'wget-list_new'), os.path.join(pkgBasePath, pkgDb, 'wget-list'))
        
    # remove package entry from data base
    if pkgDb == 'extra':
        newDb = codecs.open(os.path.join(pkgDir, '..', '..', 'extra.package.list_new'), "w", "utf-8")
        for fl in codecs.open(os.path.join(pkgDir, '..', '..', 'extra.package.list'), "r", "utf-8").readlines():
            pkgEntryName, pkgEntryVersion, pkgEntryArchive, pkgEntryDesc = parsePkgInfo(fl)
            if pkgEntryName != pkgName:
                newDb.write("%s\n" % fl.strip())
        newDb.close()
        os.remove(os.path.join(pkgDir, '..', '..', 'extra.package.list'))
        shutil.move(os.path.join(pkgDir, '..', '..', 'extra.package.list_new'), os.path.join(pkgDir, '..', '..', 'extra.package.list'))
    
    # ask to add new package into data base
    sys.stderr.write('Package ready [y/n]?: ')
    answer = sys.stdin.readline().strip()
    if not answer.startswith('y'):
        return False
    
    wgetF = file(os.path.join(pkgDir, '..', 'wget-list'), 'a')
    wgetF.write('\n')
    for fl in file(os.path.join(pkgBaseDir, 'wget-list'), 'r').readlines():
        if fl and not fl.startswith("#"):
            wgetF.write("%s" % fl)
    wgetF.close()
    
    pkgInfo = file(os.path.join(pkgBasePath, 'extra.package.list'), 'a')
    pkgInfo.write('\n')
    pkgInfo.writelines(file(os.path.join(pkgBaseDir, '%s.info' % pkgName), 'r').readlines())
    pkgInfo.close()
    
    return True




def addPkgAndDeps(pkgName):
    pkgHtmlName = getHtmlPkgName(pkgName)
    pkgDbInfo = getPkgInfo(pkgName)
    pkgDb = pkgDbInfo[4]
    pkgHtmlInfo = getHtmlPkgInfo(pkgHtmlName)
    
    if pkgDbInfo[0] == None:
        pkgDb = 'extra'
        sys.stderr.write('%s not in package data base' % pkgName)
        if pkgHtmlInfo[0] != None:
            pkgVersion = pkgHtmlInfo[1]
            pkgArchive = pkgHtmlInfo[2]
        else:
            sys.stderr.write(' nor in html data base found ...\n')
            sys.stderr.write('Type in package version (without blanks and hyphens) [1.0]: ')
            pkgVersion = sys.stdin.readline().strip()
            if not pkgVersion:
                pkgVersion = "1.0"
            sys.stderr.write('Type in package archive file (without extension) [%s-%s]: ' % (pkgName, pkgVersion))
            pkgArchive = sys.stdin.readline().strip()
            if not pkgArchive:
                pkgArchive = "%s-%s" % (pkgName, pkgVersion)
            sys.stderr.write("\nPackage summary:\n")
            sys.stderr.write("Name:    %s\n" % pkgName)
            sys.stderr.write("Version: %s\n" % pkgVersion)
            sys.stderr.write("Archive: %s(.tar|.tgz|.tar.gz|.tar.xz|.tar.bz2)\n" % pkgArchive)
            sys.stderr.write('Information OK [y/n]?: ')
            answer = sys.stdin.readline().strip()
            if not answer.startswith('y'):
                return False
            
        sys.stderr.write('\nadding package %s-%s to package data base ...\n' % (pkgName, pkgVersion))
        if not makePkg(pkgName, pkgVersion, pkgArchive, not pkgHtmlInfo[0]):
            return False
    else:
        if pkgHtmlInfo[0] != None:
            instTxtChange = (not os.path.isfile(os.path.join(pkgBasePath, pkgDb, pkgName, 'pkgbase', 'install.txt')) or
                             not filecmp.cmp(os.path.join(pkgBasePath, pkgDb, pkgName, 'pkgbase', 'install.txt'), os.path.join(getHtmlPkgPath(pkgHtmlName), 'install.txt'), shallow=0))
            if pkgDbInfo[1] != pkgHtmlInfo[1] or instTxtChange:
                sys.stderr.write('version and/or installation instruction change in html and package data base: %s %s --> %s ...\n' % (pkgName, pkgDbInfo[1], pkgHtmlInfo[1]))
                # TODO: handle update...
            else:
                sys.stderr.write('version in html and package data base are the same: %s %s ...\n' % (pkgName, pkgDbInfo[1]))
        else:
            sys.stderr.write('%s not in html data base found ... check manually for updates\n' % pkgName)

    # processing dependencies
    sys.stderr.write('Process package %s dependencies...\n' % pkgName)
    depFile = os.path.join(pkgBasePath, pkgDb, pkgName, 'deps.pkg')
    if os.path.isfile(depFile):
        deps = file(depFile, 'r')
        for fl in deps.readlines():
            depName, depVersion = parsePkgDeps(fl)
            if depName:
                sys.stderr.write('depending package %s found\n' % depName)
                if not addPkgAndDeps(depName):
                    sys.stderr.write('aborted on dependency %s...\n' % depName)
                    return False
                else:
                    sys.stderr.write('dependency %s added\n' % depName)
    return True


if __name__ == '__main__':
    if len(sys.argv) <= 1:
        sys.stderr.write('%s: missing package name as argument\n' % sys.argv[0])
        sys.stderr.write('Enter the package name: ');
        pkgName = sys.stdin.readline().strip()
        if not pkgName:
            exit(1)
    else:
        pkgName = sys.argv[1]
    
    # pkgDbInfo = getPkgInfo(pkgName)
    # pkgHtmlInfo = getHtmlPkgInfo(getHtmlPkgName(pkgName))
    addPkgAndDeps(pkgName)
    pass
